<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body ng-controller="Ctrl as c">
<input type="text" ng-model="name"/>
<br/>

<p>
    $digest:
    <span>Delayed Message: {{message}}</span>
</p>

<p>
    $eval:
    <input type="number" ng-model="a"/> +
    <input type="number" ng-model="b"/> =
    <span ng-bind="getResult()"></span>
</p>
<script src="../../../../../../../bower_components/angular/angular.js"></script>
<script>
    var app = angular.module('app', []);
    console.info(app);
    app.controller('Ctrl', ['$scope', '$rootScope', '$timeout', function ($scope, $rootScope, $timeout) {
        var isolate = false, // 是否是隔绝的 scope
                timeout = 1000;
        $rootScope.name = 'Tom';
        $scope.name = 'Jim';

        // $new
        var scope1 = $scope.$new(isolate, $scope); // isolate 为 false 时新创建的 scope 的 prototype 会指向指定的 $new 方法的调用者; 为 true 时没有 prototype(为{}). $parent(第二个参数) 还是指向指定的对象
        isolate = true;
        var scope2 = $scope.$new(isolate, $scope);
        console.log(scope1, scope2.name); // scope1.name -> Jim  scope2.name -> undefined

        // $watch, 第一个参数: 监控的表达式; 第二个参数: 监控的值发生改变时触发的方法; 第三个参数: 监控的是对象时(非字符串);
        $scope.obj = {name: 'Jim', age: 24};
        $scope.$watch('name', function (newValue, oldValue, scope) {
            console.log('$scope === scope =>', $scope === scope);
        });
        $scope.$watch('obj', function () {
            console.log(arguments);
        }, true);
        $$timeout(function () {
            $scope.obj.age = 25;
            console.log('change');
        });

        // $watchGroup 第一个参数: 监控的表达式数组或函数(函数需返回数组); 第二个参数: 监控的某一个值发生改变时触发的方法;
        $scope.value1 = '';
        $scope.value2 = '';
        $scope.value3 = '';
        $scope.$watchGroup(['value1', 'value2', 'value3'], function (nv, ov) {
            console.log(nv, ov);
        });
        $$timeout(function () {
            $scope.value1 = 'value1';
        });
        $$timeout(function () {
            $scope.value2 = 'value2';
        });
        $$timeout(function () {
            $scope.value3 = 'value3';
        });

        // $watchCollection 跟 $watch 一样，不过监控的表达式表示一个数组或对象(和 $watch(.., true)一样但是深度只监控一层)
        // 数组
        $scope.arr = [{id: 1}];
        $scope.$watchCollection('arr', function (nv, ov) {
            console.log('$watchCollection', nv[0].id, ov);
        });
        $scope.$watch('arr', function (nv, ov) {
            console.log('$watch', nv, ov);
        });
        $scope.$watch('arr', function (nv, ov) {
            console.log('$watch(true)', nv, ov);
        }, true);
        $$timeout(function () {
            $scope.arr.push(1);// 此时不会触发 $watch, 使用 $scope.arr = [1] 才会触发
        });
        $$timeout(function () {
            $scope.arr[0].id = 2;// 此时不会触发 $watch 和 $watchCollection
        });
        // 对象
        $scope.user = {id: 1, name: 'Sham', dog: {age: 1}};
        $scope.$watchCollection('user', function () {
            console.log('$watchCollection user');
        });
        $scope.$watch('user', function () {
            console.log('$watch user');
        });
        $scope.$watch('user', function () {
            console.log('$watch(true) user');
        }, true);
        $$timeout(function () {
            $scope.user.id = 2; // 不会触发$watch
        });
        $$timeout(function () {
            $scope.user.dog.age = 2; // 只触发 $watch(true)
        });

        // $digest 手动检查$scope(以及$scope的child)上的值的变化情况, 如果在watcher的 listener函数被执行时可能引起的model变化。因此，$digest循环会持续运行直到model不再发生变化，或者$digest循环的次数达到了10次。因此，尽可能地不要在listener函数中修改model。
        // $digest循环最少也会运行两次，即使在listener函数中并没有改变任何model。它会多运行一次来确保models没有变化。

        // $destroy 销毁掉$scope

        // $eval 就相当于eval. 执行字符串或函数形式的表达式
        $scope.a = 0;
        $scope.b = 0;
        // 字符串形式
        $scope.getResult = function () {
            return $scope.$eval('a+b'); // 相当于 $scope.a + $scope.b; 注意是当前scope下的a/b
        };
        // 函数形式
//        $scope.getResult = function () {
//            return $scope.$eval(function (scope) {
//                return scope.a + scope.b;
//            });
//        };

        // $evalAsync 在directive中时表示在angular操作dom之后浏览器渲染之前执行; 在controller中时表示在angular操作dom之前及浏览器渲染之前执行. ($timeout 是在angular操作dom之前及浏览器渲染之后执行.)

        // $apply 使用 $eval 执行一段表达式, 如果在执行表达式时报错则转发到 $exceptionHandler service 最后调用 $rootScope 的 $digest() 方法.

        // $applyAsync 与 $evalAsync 差不多但是只在$rootScope上执行一次

        // $on(name, listener) 当触发了name事件时执行 listener (listener => function(event, ...args){..})
        // event => targetScope: 是哪一个scope触发的此事件; currentScope:
        $scope.$on('myEvent', function (event, data1, data2) {
            console.group('---$on---');
            console.log(event);
            console.log('传递的数据 => ', data1, data2);
            console.log('scope1 === event.targetScope =>', scope1 === event.targetScope);
            console.groupEnd();
        });
        scope1.$emit('myEvent', 'data1', 'data2');

        // $emit(name, args) 向上级(父级)及上级的上级通知

        // $broadcast(name, args) 向下级(child)及下级的下级通知

        function $$timeout(fn) {
            $timeout(fn, timeout);
            timeout += 500;
        }
    }]);
</script>
</body>
</html>